structure FindEscape : sig
    type escEnv
    val escapeEnv : escEnv
    val findEscape : Absyn.exp -> unit
end =
struct
   structure A = Absyn
   structure E = ErrorMsg
   structure S = Symbol

   type depth = int

   type escEnv = (depth * bool ref) S.table

   val escapeEnv= S.empty
   (* traverseExp: escEnv * int * A.exp -> unit *)
   fun traverseExp (env, d, e) = 
        (case  e of
	     A.OpExp{left,oper=A.PlusOp,right,pos} =>
		( traverseExp(env,d,left)
		;traverseExp( env,d,right)
                )
	      | A.OpExp{left,oper=A.MinusOp,right,pos} =>
		( traverseExp(env,d,left)
		; traverseExp( env,d,right)
		)
	      |A.OpExp{left,oper=A.TimesOp,right,pos} =>
		( traverseExp(env,d,left)
		; traverseExp( env,d,right)
		)
	      |A.OpExp{left,oper=A.DivideOp,right,pos} =>
	      	(traverseExp(env,d,left)
		;traverseExp( env,d,right)
		)
	      |A.OpExp{left,oper=A.EqOp,right,pos} =>
	      	 (traverseExp(env,d,left)
		  ;traverseExp( env,d,right)
		 )
	      |A.OpExp{left,oper=A.LtOp,right,pos} =>
	      	(traverseExp(env,d,left)
		; traverseExp( env,d,right)
		)
	      |A.OpExp{left,oper=A.LeOp,right,pos} =>
	      	(traverseExp(env,d,left)
		; traverseExp( env,d,right)
		)
	      |A.OpExp{left,oper=A.GtOp,right,pos} =>
	      	(traverseExp(env,d,left)
		; traverseExp( env,d,right)
		)
	      |A.OpExp{left,oper=GeOp,right,pos}=>
	      	(traverseExp(env,d,left)
		;traverseExp( env,d,right)
		)
	      |A.IfExp{test, then', else', pos}=>
	      	(traverseExp (env,d,test);
		 traverseExp(env,d,then'); 
                case else' of 
		SOME else'=> traverseExp(env,d,else')		   
		)
	      |A.WhileExp{test, body, pos}=>
	      	    ( traverseExp (env,d,test)
		    ;traverseExp(env,d,body)		    
		    )
             |A.ForExp {var, escape=escape, lo, hi, body, pos} =>	      	     
		(S.enter(env,var,(d, ref false));
		traverseExp(env,d,lo);
		traverseExp (env,d,hi);
		traverseExp (env,d,body)
		)
             |A.SeqExp ((exp,pos)::nil) => traverseExp(env,d,exp)
             |A.SeqExp ((exp,pos)::rest) => (traverseExp(env,d,exp); traverseExp(env,d,A.SeqExp rest))
	     |A.AssignExp {var=i, exp=exp, pos=pos} =>
	     	     (traverseVar(env,d,i);
		      traverseExp(env,d,exp)
		     )
	     |A.ArrayExp {typ=typ, size=size, init=init, pos=pos} =>
                 (traverseExp (env,d,init);
		 traverseExp (env,d,size)
)
	
	     |A.RecordExp{fields=fields, typ=typ,pos} =>
	       ( case fields of 
		     hd::nil => traverseExp(env,d,#2 hd)
	             |hd::rest => (traverseExp(env,d,#2 hd); traverseExp(env,d,A.RecordExp{fields=rest,typ=typ,pos=pos}))
		 
		 )
	     |A.LetExp{decs=decs, body=body, pos=pos} =>
		(
		  traverseDecs(env,d,decs)
		 ;traverseExp(env,d,body)
                )
       	     |A.VarExp var => traverseVar( env, d, var)
       	     |_ => ()
	)          	   

   (* traverseVar : escEnv * int * A.var -> unit *)
   and traverseVar (env, d, v) = 
       case v of
       	    A.SimpleVar (symbol,pos) => 
	    	      		   let val tyoption=S.look (env, symbol)
      				   in  
			 	   ( case tyoption of
	     	  	   	     	   NONE => (E.error pos "type is not defined"; ())
	    	  		 	  |SOME (depth, bolref)=> if d>depth then (S.enter(env,symbol,(depth, ref true));() )else ()  )
     				    end 
            |A.FieldVar (var,symbol,pos)=> 
	      	       		    let val tyoption=S.look (env, symbol)
      				   in  
			 	   (( case tyoption of
	     	  	   	     	   NONE => (E.error pos "type is not defined"; ())
	    	  		 	   |SOME (depth, bolref)=> if d>depth then (S.enter(env,symbol,(depth, ref true)); ()) else ()  );
				    traverseVar (env, d,var))
     				    end 

            | A.SubscriptVar (var,exp, pos) =>
	      		   ( traverseVar(env, d,var);
			     traverseExp(env,d,exp)
			      
        
)
   (* traverseDecs : escEnv * int * A.dec list -> escEnv *)
   and traverseDecs (env, d, ds) = 
       let fun enterbinding (decl,escenv)=
       	  (case decl of
	   A.VarDec ({name=name, escape=escape,typ=typ,init=init,pos=pos})=>  S.enter(escenv, name,(d, ref false))
	   |A.TypeDec(hd::rest) =>   enterbinding (A.TypeDec(rest),S.enter(escenv,#name hd,(d, ref false)))
	   |A.FunctionDec(hd::rest) => (let val escEnv2= S.enter(escenv,#name hd,(d, ref false))
	   		   	       	   val paramlist= #params hd;
					   val escEnv3= foldl (fn (param,escEnvp)  => S.enter(escEnvp,#name param,(d, ref false))) escEnv2 paramlist
 				       in 
				       	  enterbinding(A.FunctionDec(rest),escEnv3)				       	  
				       end)
  )
      in
	foldl enterbinding env  ds
      end


   fun findEscape e = traverseExp (escapeEnv, 0, e)
end
